import * as fs from 'fs';
import * as path from 'path';
import execSync from 'child_process';
import log from 'ololog'

const WS_SUPPORT = true; // set to true when WS support is added

const foldersToSearch = [
    './go/v4'
];

const filesToDelete =[
    './go/tests/base/test.structs.go',
    './go/tests/base/test.types.rest.go',
    './go/tests/base/test.types.pro.go'
]

const whiteListFolders = [
    'go/v4/protoc'
]


function capitalizeFirstLetter(string: string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function deleteFilesRecursively(directory: string, exchangesToKeep: string[]): void {
    if (!fs.existsSync(directory)) {
        console.warn(`Directory not found: ${directory}`);
        return;
    }

    fs.readdirSync(directory).forEach(file => {
        const fullPath = path.join(directory, file);

        const fileWithoutExt = path.parse(file).name;

        const fileExtension = path.extname(file);
        if (fileExtension !== '.go' && !fs.statSync(fullPath).isDirectory()) {
            return; // Skip non-Go files
        }

        if (file.startsWith('exchange')) {
            return; // Always keep exchange.go and exchange_X.go files
        }

        if (whiteListFolders.some(folder => fullPath.startsWith(folder))) {
            return; // Always keep files in whitelisted folders
        }

        if (fs.statSync(fullPath).isDirectory()) {
            deleteFilesRecursively(fullPath, exchangesToKeep);
        } else if (!exchangesToKeep.some(exchange => (fileWithoutExt === exchange || fileWithoutExt.startsWith(exchange + '_')))) {
            try {
                fs.unlinkSync(fullPath);
                log.red(`Deleted: ${fullPath}`);
            } catch (error) {
                log.red(`Failed to delete ${fullPath}:`, error);
            }
        }
    });
}

function createExchangeDynamicFile(exchanges: string[], ws = false) {

    const dynamicPath = ws ? './go/v4/pro/exchange_dynamic.go' : './go/v4/exchange_dynamic.go';

    const pack = ws ? 'ccxtpro' : 'ccxt';
    const imports = ws ? 'import ccxt "github.com/ccxt/ccxt/go/v4"' : ''
    const prefix = ws ? 'ccxt.' : '';

    const caseStatements = exchanges.map(exchange => {
        const statement = `    case "${exchange}":
            ${exchange}Itf := New${capitalizeFirstLetter(exchange)}Core()
            ${exchange}Itf.Init(exchangeArgs)
            return ${exchange}Itf, true`;
        if (!ws) {
            return statement;
        }
        return fs.existsSync('./ts/src/pro/' + exchange + '.ts') ? statement : '';
        }).join('\n');

const ExchangeStatement = `
    case "Exchange":
		ExchangeItf := &Exchange{}
		ExchangeItf.Init(exchangeArgs)
		return ExchangeItf, true
`


    const template =`
package ${pack}
${imports}
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code



func DynamicallyCreateInstance(exchangeId string, exchangeArgs map[string]interface{}) (${prefix}ICoreExchange, bool) {
    switch exchangeId {
${ws ? '' : ExchangeStatement}
${caseStatements}
    default:
        return nil, false
    }
}`

    fs.writeFileSync(dynamicPath, template.trim());
    log.green(`Created: ${dynamicPath}`);
}


function createExchangeTypedInterfaceFile(exchanges: string[]) {

    const path = './go/v4/exchange_typed_interface.go'

    const fileContent = fs.readFileSync(path, 'utf-8');

    const regex = /(type IExchange interface {(\n|.)+\n}[\n]+)func CreateExchange/

    const interfaceMatch = fileContent.match(regex) as RegExpMatchArray;

    // Modify the file content as needed

    const caseStatements = exchanges.map(exchange => `       case "${exchange}":
           itf := New${capitalizeFirstLetter(exchange)}(options)
           return itf`).join('\n    ');

    const template = `
package ccxt
import "strings"
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
${interfaceMatch[1]}

func CreateExchange(exchangeId string, options map[string]interface{}) IExchange {
    exchangeId = strings.ToLower(exchangeId)
    switch exchangeId {
    case "exchange":
        itf := NewExchangeTyped(nil)
        return itf
${caseStatements}
    default:
        return nil
    }
}
`
    fs.writeFileSync(path, template.trim());
}


function createWsExchangeTypedInterfaceFile(exchanges: string[]) {

    const path = './go/v4/pro/exchange_typed_interface.go'

    const interfaceWs = [
            'type IExchange interface {',
            '    ccxt.IExchange',
            '}'
        ].join('\n');

    const caseStatements = exchanges.map(exchange => {
        return fs.existsSync('./ts/src/pro/' + exchange + '.ts') ? `       case "${exchange}":
           itf := New${capitalizeFirstLetter(exchange)}(options)
           return itf` : ''}).join('\n    ');

    const template = `
package ccxtpro
import (
    "strings"
    ccxt "github.com/ccxt/ccxt/go/v4"
)

// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

${interfaceWs}

func CreateExchange(exchangeId string, options map[string]interface{}) ccxt.IExchange {
    exchangeId = strings.ToLower(exchangeId)
    switch exchangeId {
${caseStatements}
    default:
        return nil
    }
}
`
    fs.writeFileSync(path, template.trim());
}


function transpileAndGenerateAPI(exchanges: string[]) {
    execSync.execSync(`tsx ./build/generateImplicitAPI.ts`, { stdio: 'inherit' });
    for (const exchange of exchanges) {
        execSync.execSync(`tsx ./build/goTranspiler.ts ${exchange}`, { stdio: 'inherit' });
        const WS_EXISTED = fs.existsSync('./ts/src/pro/' + exchange + '.ts');
        if (WS_SUPPORT && WS_EXISTED) {
            execSync.execSync(`tsx ./build/goTranspiler.ts ${exchange} --ws`, { stdio: 'inherit' });
        }
    }

}

function main() {
    const argsRaw = process.argv.slice(2);

    if (argsRaw.length < 1) {
        console.error("Usage: tsx granular-go-build.ts <exchange1> <exchange2> ...");
        process.exit(1);
    }
    const args = [...new Set(argsRaw)];
    transpileAndGenerateAPI(args)
    foldersToSearch.forEach(folder => deleteFilesRecursively(folder, args));
    createExchangeDynamicFile(args);
    if (WS_SUPPORT) {
        createExchangeDynamicFile(args, true);
    }
    createExchangeTypedInterfaceFile(args);
    createWsExchangeTypedInterfaceFile(args);

    for (const filePath of filesToDelete) {
        if (fs.existsSync(filePath)) {
            try {
                fs.unlinkSync(filePath);
                log.red(`Deleted: ${filePath}`);
            } catch (error) {
                log.red(`Failed to delete ${filePath}:`, error);
            }
        }
    }
    const languageSpecificFile = './go/tests/base/test.languageSpecific.go';
    if (fs.existsSync(languageSpecificFile)) {
        let fileContent = fs.readFileSync(languageSpecificFile, 'utf-8');
        const regex = /TestStructs\(\)/;
        fileContent = fileContent.replace(regex, '');
        fs.writeFileSync(languageSpecificFile, fileContent);
        log.green(`Modified: ${languageSpecificFile}`);
    }

    log.bright.cyan("Done! You can now build the Go project (tests/cli/etc) with 'go build' command.");
}

main();
